package io.github.libkodi.basic_gateway.router;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Iterator;

import io.github.libkodi.basic_gateway.context.Context;
import io.github.libkodi.basic_gateway.tools.ClassUtils;
import io.vertx.core.Future;
import io.vertx.core.Promise;
import io.vertx.ext.web.RoutingContext;

public class RouteIntercepterImpl {
	private ArrayList<Object> list = new ArrayList<Object>();
	private Object mutex;
	private Context ctx;
	private Method requestMethod;
	
	public RouteIntercepterImpl(Context ctx) throws NoSuchMethodException, SecurityException {
		this.mutex = this;
		this.ctx = ctx;
		this.requestMethod = RouteIntercepter.class.getDeclaredMethod("request", RoutingContext.class, Method.class);
	}

	public Future<Void> push(Class<? extends RouteIntercepter> cls) {
		return Future.future(promise -> {
			synchronized (mutex) {
				try {
					Object instance = ClassUtils.newInstance(cls);
					
					ClassUtils.injectContext(instance, ctx).onSuccess(v -> {
						try {
							Method method = RouteIntercepter.class.getDeclaredMethod("init");
							method.invoke(instance);
							
							list.add(instance);
							promise.complete();
						} catch (Exception e) {
							promise.fail(e);
						}
					}).onFailure(e -> {
						promise.fail(e);
					});
				} catch (Exception e) {
					promise.fail(e);
				}
			}
		});
	}

	public Future<Void> check(RoutingContext context, Method method) {
		return Future.future(promise -> {
			Iterator<Object> iter = list.iterator();
			execCheckRequest(context, iter, promise, method);
		});
	}

	@SuppressWarnings("unchecked")
	private void execCheckRequest(RoutingContext context, Iterator<Object> iter, Promise<Void> promise, Method method) {
		if (iter.hasNext()) {
			try {
				Object instance = iter.next();
				Future<Void> ret = (Future<Void>) requestMethod.invoke(instance, context, method);
				
				ret.onSuccess(v -> {
					if (!context.response().ended()) {
						execCheckRequest(context, iter, promise, method);
					} else {
						promise.complete();
					}
				}).onFailure(e -> {
					if (!context.response().ended()) {
						promise.fail(e);
					}
				});
			} catch (Exception e) {
				promise.fail(e);
			}
		} else {
			promise.complete();
		}
	}
	
}
